function widget:GetInfo()
	return {
	name      = "Hybrid Overhead camera", --v4
	desc      = "Hybrid Overhead camera mode",
	author    = "Regret",
	date      = "August 19, 2009", --last change August 28,2009
	license   = "GNU GPL, v2 or later",
	layer     = 1000,
	enabled   = false,
	handler   = true,
	}
end

local maxpx,maxpz = Game.mapX*512,Game.mapY*512 --camera movement limits
local maxrx = 89 /180*math.pi --max angle
local minpx = -89 /180*math.pi --min angle
local cameradistance = 2000 --start zoom distance
local changesmoothness = 0.05
local mindistance = 100
local maxdistance = 20000
local zoom_to_cursor = true
local draw_J_movement_crosshair = true
local center_cursor_on_J_movement = true
local use_order_cursor_for_J_movement = false
local center_map_on_zoomout = true
local use_midclick_to_rotate = true
local allow_rotation_with_keys = false

local midclickmaxtime = 0.5 --how long until a click gets dropped after mousepress, in seconds

local sin = math.sin
local cos = math.cos
local sqrt = math.sqrt
local min = math.min
local max = math.max

local clock = os.clock

local sGetCameraState = Spring.GetCameraState
local sSendCommands = Spring.SendCommands
local sSetCameraState = Spring.SetCameraState
local sGetConfigInt = Spring.GetConfigInt
local sIsAboveMiniMap   = Spring.IsAboveMiniMap
local sGetGroundHeight = Spring.GetGroundHeight
local sGetModKeyState = Spring.GetModKeyState
local sSetMouseCursor = Spring.SetMouseCursor
local sTraceScreenRay = Spring.TraceScreenRay --warning: mousey is vsy-mousey in this widget, i.e. 0,0 top left

local vsx,vsy = widgetHandler:GetViewSizes()
if (vsx == 1) then --hax for windowed mode
	vsx,vsy = Spring.GetWindowGeometry()
end
function widget:ViewResize(viewSizeX, viewSizeY)
	vsx,vsy = widgetHandler:GetViewSizes()
end

local _sWarpMouse = Spring.WarpMouse
local sWarpMouse = function(x,y)
	_sWarpMouse(x,vsy-y)
end

local _sGetMouseState = Spring.GetMouseState
local sGetMouseState = function() 
	local x,y = _sGetMouseState()
	return x,vsy-y
end

local track = false
local originalcamera = false
local campx,campy,campz = 0,0,0 --camera pointed coords
local lastcx,lastcy,lastcz = 0,0,0
local warponce = true
local unwarpedx,unwarpedy = nil,nil
local edgemove = false
local rotateactive = false
local smoothscrollenabled = false
local updatecounter = 0
local midclicktime = 0

local function forceFreeCam()
	if (camera.name ~= "free") then --fixme: add check for fps mode
		sSendCommands("viewfree")
		camera = sGetCameraState()
		camera.gndOffset = 0
		camera.gravity = 0
		camera.slide = 0
		camera.scrollSpeed = 0
		camera.tiltSpeed = 0
		camera.velTime = 0
		camera.avelTime = 0
		camera.autoTilt = 0
		camera.goForward = -1
		camera.invertAlt = -1
		camera.gndLock = -1
		sSetCameraState(camera,0)
	end
end

local function warpMouse()
	local dx = (mx-unwarpedx)
	local dz = (my-unwarpedy)
	sWarpMouse(unwarpedx,unwarpedy)
	mx,my = sGetMouseState()
	return dx,dz
end

local function adjustCam(czoomx,czoomz)
	lastcy = sin(camera.rx)*cameradistance
	local grounddistance = sqrt(cameradistance*cameradistance-lastcy*lastcy)
	lastcx = (grounddistance*sin(camera.ry))
	lastcz = (grounddistance*cos(camera.ry))
	
	if (czoomx) then
		campx = campx + czoomx
		campz = campz + czoomz
	end
	
	camera.px = campx - lastcx
	camera.pz = campz - lastcz
	camera.py = campy + lastcy
end

local function rotateCamera(conditiononly)
	local alt,ctrl,meta,shift = sGetModKeyState()
	local condition = meta
	if (use_midclick_to_rotate) then
		condition = ((not rotateactive) and meta) or rotateactive
	end
	
	if (conditiononly) then
		if (condition) then
			return true
		else return end
	else if (not rotateCamera(true)) then return end end

	local speedfactor = -1/250
	if (shift) then
		speedfactor = speedfactor * 3
	end
	local dx,dz = 0,0
	if ((not rotateactive) and allow_rotation_with_keys and (edgemoveup or edgemovedown or edgemoveright or edgemoveleft)) then --keyboard
		local spd = 750
		if (edgemoveleft) then
			dx = -spd*speedfactor
		elseif (edgemoveright) then
			dx = spd*speedfactor
		end
		
		if (edgemoveup) then
			dz = -spd*speedfactor
		elseif (edgemovedown) then
			dz = spd*speedfactor
		end
	else
		if (warponce) then
			mx,my = sGetMouseState()
			if (unwarpedx == nil) then
				unwarpedx,unwarpedy = mx,my
			end
			warponce = nil
		end
		dx,dz = warpMouse()
	end
	
	dx = dx*speedfactor
	dz = dz*speedfactor
	
	camera.ry = camera.ry + dx
	camera.rx = camera.rx + dz
	
	camera.rx = min(max(camera.rx,minpx),maxrx)
	
	adjustCam()
end

local function trackCamera(conditiononly)
	if (conditiononly) then
		if (track and ((lastmx~=mx) or (lastmy~=my))) then
			return true
		else return end
	else if (not trackCamera(true)) then return end end
	
	local speedfactor = cameradistance/250
	if (shift) then
		speedfactor = speedfactor * 3
	end
	local dx,dz = 0,0
	
	if (warponce) then
		sWarpMouse(vsx*0.5,vsy*0.5)
		mx,my = sGetMouseState()
		unwarpedx,unwarpedy = mx,my
		warponce = nil
	end
	
	if (center_cursor_on_J_movement) then
		local pmx,pmy = mx,my
		sWarpMouse(vsx*0.5,vsy*0.5)
		mx,my = sGetMouseState()
		unwarpedx,unwarpedy = mx,my
		dx = pmx-mx
		dz = pmy-my
	else
		dx,dz = warpMouse()
	end
	
	dx = dx*speedfactor
	dz = dz*speedfactor
	
	local c = cos(camera.ry)
	local s = sin(camera.ry)
	campx = campx +dx*c +dz*s
	campz = campz -dx*s +dz*c
end

local function edgemoveCamera(conditiononly)
	local et = 1 --edgemove treshhold
	
	if (conditiononly) then
		if ((not (allow_rotation_with_keys and rotateCamera(true)))
		and((edgemoveup or edgemovedown or edgemoveright or edgemoveleft)
		or ((edgemove==1) and ((mx <= et) or (mx >= vsx-et) or (my <= et) or (my >= vsy-et))))) then
			return true
		else return end
	else if (not edgemoveCamera(true)) then return end end

	local dx,dz = 0,0
	local speedfactor = cameradistance/250
	local alt,ctrl,meta,shift = sGetModKeyState()
	if (shift) then
		speedfactor = speedfactor * 3
	end

	if (edgemoveup or edgemovedown or edgemoveright or edgemoveleft) then --keyboard
		if (edgemoveleft) then
			dx = -keymovespeed*speedfactor
		elseif (edgemoveright) then
			dx = keymovespeed*speedfactor
		end
		
		if (edgemoveup) then
			dz = -keymovespeed*speedfactor
		elseif (edgemovedown) then
			dz = keymovespeed*speedfactor
		end
	else
		if (mx <= et) then --mouse
			dx = -edgemovespeed*speedfactor
		elseif (mx >= vsx-et) then
			dx = edgemovespeed*speedfactor
		end
		
		if (my <= et) then
			dz = -edgemovespeed*speedfactor
		elseif (my >= vsy-et) then
			dz = edgemovespeed*speedfactor
		end
	end
	
	local c = cos(camera.ry)
	local s = sin(camera.ry)
	campx = campx +dx*c +dz*s
	campz = campz -dx*s +dz*c
end

function widget:Update()
	--for smoothcamera compatibility
	if (updatecounter == 200) then
		updatecounter = 0
		smoothscrollenabled = false
		for i,w in ipairs(widgetHandler.widgets) do
			if (w:GetInfo().name == "SmoothScroll") then
				smoothscrollenabled = true
			end
		end
	end
	updatecounter=updatecounter+1
	----------

	forceFreeCam()
	mx,my = sGetMouseState()
	
	if ((not use_order_cursor_for_J_movement) and center_cursor_on_J_movement and track) then
		sSetMouseCursor("none")
	end

	local process = rotateCamera(true) or trackCamera(true) or edgemoveCamera(true)
	
	--for F3 (playerpoint) compatibility
	local tempcamera = sGetCameraState()
	if (lastrealcampx == nil) then
		lastrealcampx = tempcamera.px
		lastrealcampz = tempcamera.pz
	end
	if (not lastprocess and (adjustingcamera or (lastrealcampx ~= tempcamera.px) or (lastrealcampz ~= tempcamera.pz))) then
		local _,coords = sTraceScreenRay(vsx*0.5,vsy*0.5,true)
		if (coords) then
			campx = coords[1]
			campz = coords[3]
			adjustCam()
			if (not adjustingcamera) then
				adjustingcamera = true
			else
				if ((lasttracex == campx) and (lasttracez == campz)) then
					adjustingcamera = nil
				end
			end
			
			if (adjustingcamera) then
				lasttracex = campx
				lasttracez = campz
			else
				lasttracex = nil
				lasttracez = nil
			end
		end
	else
		adjustingcamera = nil
	end
	----------
	
	
	if (process) then
		rotateCamera()
		trackCamera()
		edgemoveCamera()
		
		campx = min(max(campx,0),maxpx)
		campz = min(max(campz,0),maxpz)
		
		campy = sGetGroundHeight(campx,campz)
		
		camera.px = campx - lastcx
		camera.pz = campz - lastcz
		camera.py = campy + lastcy
		
		sSetCameraState(camera,changesmoothness)
	else
		unwarpedx,unwarpedy = nil,nil
		warponce = true
	end
	lastmx,lastmy = mx,my
	
	
	--for F3 (playerpoint) compatibility
	lastprocess = process
	if (process) then
		lastrealcampx = camera.px
		lastrealcampz = camera.pz
	else
		lastrealcampx = tempcamera.px
		lastrealcampz = tempcamera.pz
	end
	----------
end

function widget:MouseWheel(up,value)
	lastprocess = true --for F3 (playerpoint) compatibility

	local alt,ctrl,meta,shift = sGetModKeyState()
	local zoomdirection = (zoomspeed > 0)
	
	if (alt) then 
		if (up == zoomdirection) then
			cameradistance = mindistance*4 --zoom in a lot
		else
			cameradistance = maxdistance*0.75 --zoom out a lot
			camera.rx = minpx
			camera.ry = math.pi
		end
		adjustCam()
		sSetCameraState(camera,changesmoothness)
		return
	end
	
	local change = zoomspeed*value*cameradistance/250
	if (shift) then -- shift accelerates zoom speed 3 times
		change = change * 3
	end
	cameradistance = max(min(cameradistance+change,mindistance),maxdistance)
	
	local zoominscale = 1/4
	local zoomoutscale = 1/64
	
	if (up == zoomdirection) then
		if (center_map_on_zoomout) then
			adjustCam((Game.mapSizeX/2-campx)*zoomoutscale,(Game.mapSizeZ/2-campz)*zoomoutscale)
		else
			adjustCam()
		end
	else
		mx,my = sGetMouseState()
		local _, coords = sTraceScreenRay(mx,vsy-my,true)
		if (coords and zoom_to_cursor) then
			adjustCam((coords[1]-campx)*zoominscale,(coords[3]-campz)*zoominscale)
		else
			adjustCam()
		end
	end
	sSetCameraState(camera,changesmoothness)
end

function widget:MousePress(mx,my,mb)
	if (mb == 2) then --mid mouse button
		midclicktime = clock()
		return true
	end
	return false
end

function widget:MouseRelease(_,_,mb)
	if ((clock()-midclicktime) > midclickmaxtime) then
		return false
	end
	
	if (mb ~= 2) then --mid mouse button
		return false
	end
	if (sIsAboveMiniMap(mx,my)) then
		return false
	end
	if (use_midclick_to_rotate) then
		rotateactive = not rotateactive
		return true
	elseif (smoothscrollenabled) then --for smoothcamera widget compatibility
		return false
	else
		track = not track
		if (track) then
			
			if (center_cursor_on_J_movement) then
				sWarpMouse(vsx*0.5,vsy*0.5)
				mx,my = sGetMouseState()
			else
				mx,my = sGetMouseState()
				unwarpedx,unwarpedy = mx,my
			end
			
			sSendCommands("trackoff")
		end
		return true
	end
end

function widget:KeyPress(key,isRepeat)
	if (track) then
		if (key == 27) then --ESC
			track = false
			return true
		end
	end
	
	if (key == 273) then --uparrow
		edgemoveup = true
	elseif (key == 274) then --downarrow
		edgemovedown = true
	elseif (key == 275) then --rightarrow
		edgemoveright = true
	elseif (key == 276) then --leftarrow
		edgemoveleft = true
	end
end

function widget:KeyRelease(key)
	if (key == 273) then --uparrow
		edgemoveup = nil
	elseif (key == 274) then --downarrow
		edgemovedown = nil
	elseif (key == 275) then --rightarrow
		edgemoveright = nil
	elseif (key == 276) then --leftarrow
		edgemoveleft = nil
	end
end

local glColor = gl.Color
local glRect = gl.Rect
local glResetState = gl.ResetState
local glResetMatrices = gl.ResetMatrices
function widget:DrawScreen()
	if (draw_J_movement_crosshair and center_cursor_on_J_movement and track) then
		glResetState()
		glResetMatrices()
		
		glColor(1,1,1,0.5)
		
		glRect(vsx*0.5,vsy*0.5-10,vsx*0.5+1,vsy*0.5+11) -- |
		glRect(vsx*0.5-10,vsy*0.5,vsx*0.5+11,vsy*0.5+1) -- _
		
		glResetState()
		glResetMatrices()
	end
end

function widget:Initialize()
	cameradistance = -cameradistance
	mindistance = -mindistance
	maxdistance = -maxdistance
	
	mx,my = sGetMouseState()
	sSendCommands("trackoff")
	originalcamera = sGetCameraState()
	camera = sGetCameraState()
	forceFreeCam()
	campx = originalcamera.px
	campz = originalcamera.pz
	campy = sGetGroundHeight(campx,campz)
	adjustCam()
	sSetCameraState(camera,0)
	
	zoomspeed = sGetConfigInt("ScrollWheelSpeed",0)
	edgemovespeed = sGetConfigInt("OverheadScrollSpeed",10)*0.75
	keymovespeed = edgemovespeed
	if (sGetConfigInt("Fullscreen",1) == 1) then
		edgemove = sGetConfigInt("FullscreenEdgeMove",1)
	else
		edgemove = sGetConfigInt("WindowedEdgeMove",1)
	end
end

function widget:Shutdown()
	originalcamera.px = campx
	originalcamera.py = camera.py
	originalcamera.pz = campz
	sSetCameraState(originalcamera,0)
end
